<?php


namespace App\Repositories;


use Closure;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Arr;

class SaveManyHandler
{
    /**
     * 构造方法
     * SaveManyHandler constructor.
     * @param \Illuminate\Database\Eloquent\Builder $model 子表的数据模型
     * @param string $foreignField 外键字段名称  子表种的字段，用于和上级表关联的字段
     */
    public function __construct($model, $foreignField)
    {
        $this->model = $model;
        $this->foreignField = $foreignField;
    }

    /**
     * 简化方法
     * @param \Illuminate\Database\Eloquent\Builder $model 子表的数据模型
     * @param string $foreignField 外键字段名称  子表种的字段，用于和上级表关联的字段
     * @return static
     */
    static function get($model, $foreignField)
    {
        $inc = new static($model, $foreignField);
        return $inc;
    }

    /**
     * 根据上级表删除子表数据
     * 用与多级数据上级被删除的时候批量删除下级表
     * @param $ids
     */
    public function deleteByParent($ids)
    {
        if (count($this->childNode)) {
            //如果还有下级 查询当前要删除的数据ID
            $_ids = (clone $this->model)->whereIn($this->foreignField, $ids)->pluck('id');
            foreach ($this->childNode as $child) {
                //删除下级数据
                $child->deleteByParent($_ids);
            }
            //当前层级数据
            (clone $this->model)->whereIn($this->foreignField, $ids)->delete();
        } else {
            //当前层级数据
            (clone $this->model)->whereIn($this->foreignField, $ids)->delete();
        }

    }

    /**
     *
     * 保存数据 最终运行的方法（请确保设置已经进行完成）
     *
     * @param $data
     */
    public function save($data)
    {
        //如果关联字段有数据表示是修改 进行修改检查
        if ($this->foreignId) {
            $query = clone $this->model;
            //可以自定义子表数据查询
            if ($this->queryCall) {
                $this->queryCall($query);
            } else {
                //查询子表数据
                $query->where($this->foreignField, '=', $this->foreignId);
            }
            $this->oldData = $oldData = $query->get()->toArray();
            //对比数据
            $delIds = [];
            $delData = [];
            foreach ($oldData as $item) {
                //获取对比的唯一值 ，这个值相同表示修改不同表示新增或删除
                $unique = $this->getUniqueCall()((array)$item);
                if (isset($data[$unique])) {
                    //存在就是修改和不修改的问题
                    $_newItem = $data[$unique];
                    $flag = false;
                    //检查附加字段是否有修改
                    foreach ($this->additional_fields as $field) {
                        if ($item[$field] != Arr::get($_newItem, $field)) {
                            $flag = true;
                        }
                    }

                    if ($flag) {
                        //有变动
                        if ($this->modifyMode == 1) {
                            //不是修改模式直接删除新增
                            $delIds[] = $item['id'];
                        } else {
                            //修改模式直接修改
                            $temp = $this->commonData;
                            foreach ($this->additional_fields as $field) {
                                $temp[$field] = Arr::get($_newItem, $field, '');
                            }
                            (clone $this->model)->where('id', $item['id'])->update($temp);
                            //检查字节点是否有变动
                            foreach ($this->childNode as $key => $child) {
                                $child->setForeignId($item['id']);
                                $child->addCommonDataByForeignField($item['id']);
                                $child->save($data[$unique][$key]);
                            }
                            unset($data[$unique]);
                        }
                    } else {
                        //没有变动
                        //检查字节点是否有变动
                        foreach ($this->childNode as $key => $child) {
                            $child->setForeignId($item['id']);
                            $child->addCommonDataByForeignField($item['id']);
                            $child->save($data[$unique][$key]);
                        }
                        unset($data[$unique]);
                    }
                } else {
                    //删除
                    $delIds[] = $item['id'];
                    $delData[] = $item;
                }
            }
            if (count($delIds)) {
                $idArray = array_chunk($delIds, 2000);
                foreach ($idArray as $_ids) {
                    (clone $this->model)->whereIn('id', $_ids)->delete();
                    foreach ($this->childNode as $child) {
                        $child->deleteByParent($_ids);
                    }
                }
                foreach ($this->deleteHandler as $handler) {
                    $handler($delData);
                }
            }
        }
        $tempData = [];
        foreach ($data as $item) {
            $temp = $this->commonData;
            foreach ($this->additional_fields as $field) {
                $temp[$field] = Arr::get($item, $field, '');
            }
            $tempData[] = $temp;
            if (count($this->childNode)) {
                $_self_id = (clone $this->model)->insertGetId($temp);
                //检查字节点是否有变动
                foreach ($this->childNode as $key => $child) {
                    $child->setForeignId(0);//设置为零表示是添加数据，不用检查是否更新
                    $child->addCommonDataByForeignField($_self_id);
                    $child->save($item[$key]);
                }
            }
        }
        if (!count($this->childNode)) {
            $tempDataArray = array_chunk($tempData, 1000);
            foreach ($tempDataArray as $tempDataItem) {
                (clone $this->model)->insert($tempDataItem);
            }
        }
        foreach ($this->updateHandler as $handler) {
            $handler($tempData);
        }
    }

    /**
     * @return Builder
     */
    public function getModel(): Builder
    {
        return $this->model;
    }

    /**
     * @param Builder $model
     * @return SaveManyHandler
     */
    public function setModel(Builder $model): SaveManyHandler
    {
        $this->model = $model;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getForeignId()
    {
        return $this->foreignId;
    }

    /**
     * 修改时候的外键ID
     * @param mixed $foreignId
     * @return SaveManyHandler
     */
    public function setForeignId($foreignId): SaveManyHandler
    {
        $this->foreignId = $foreignId;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getForeignField()
    {
        return $this->foreignField;
    }

    /**
     * 设置关联字段的值如果设置了就按照修改处理
     * @param mixed $foreignField
     * @return SaveManyHandler
     */
    public function setForeignField($foreignField)
    {
        $this->foreignField = $foreignField;
        return $this;
    }

    /**
     * @return array
     */
    public function getDeleteHandler(): array
    {
        return $this->deleteHandler;
    }

    /**
     * 监听删除
     * @param $call
     * @return $this
     */
    public function listenDelete($call)
    {
        $this->deleteHandler[] = $call;
        return $this;
    }

    /**
     * @return array
     */
    public function getUpdateHandler(): array
    {
        return $this->updateHandler;
    }

    /**
     * @param array $updateHandler
     * @return SaveManyHandler
     */
    public function setUpdateHandler(array $updateHandler): SaveManyHandler
    {
        $this->updateHandler = $updateHandler;
        return $this;
    }

    /**
     * @return Closure
     */
    public function getUniqueCall(): Closure
    {
        return $this->uniqueCall;
    }

    /**
     * @param Closure $uniqueCall
     * @return SaveManyHandler
     */
    public function setUniqueCall(Closure $uniqueCall): SaveManyHandler
    {
        $this->uniqueCall = $uniqueCall;
        return $this;
    }

    /**
     * @return array
     */
    public function getAdditionalFields(): array
    {
        return $this->additional_fields;
    }

    /**
     * 添加关联字段的值 就是上级表的ID
     * @param $id
     */
    public function addCommonDataByForeignField($id)
    {
        $this->commonData[$this->foreignField] = $id;
    }

    /**
     * @param array $additional_fields
     * @return SaveManyHandler
     */
    public function setAdditionalFields(array $additional_fields): SaveManyHandler
    {
        $this->additional_fields = $additional_fields;
        return $this;
    }

    /**
     * 获取公共数据
     * @return array
     */
    public function getCommonData(): array
    {
        return $this->commonData;
    }

    /**
     * @param array $commonData
     * @return SaveManyHandler
     */
    public function setCommonData(array $commonData): SaveManyHandler
    {
        $this->commonData = $commonData;
        return $this;
    }

    /**
     * @return Closure
     */
    public function getQueryCall(): Closure
    {
        return $this->queryCall;
    }

    /**
     * @param Closure $queryCall
     * @return SaveManyHandler
     */
    public function setQueryCall(Closure $queryCall): SaveManyHandler
    {
        $this->queryCall = $queryCall;
        return $this;
    }

    /**
     * @return int
     */
    public function getModifyMode(): int
    {
        return $this->modifyMode;
    }

    public function getOldData()
    {
        return $this->oldData;
    }

    /**
     * 添加子节点处理
     * @param string $dataKey 数据种子节点数据的key
     * @param SaveManyHandler $child 子节点处理器
     */
    public function addChildNode(string $dataKey, SaveManyHandler $child)
    {
        $this->modifyMode = self::MODIFY_MODE_UPDATE;
        $this->childNode[$dataKey] = $child;
    }

    /**
     * 设置修改模式
     * @param int $modifyMode
     * @return SaveManyHandler
     */
    public function setModifyMode(int $modifyMode): SaveManyHandler
    {
        $this->modifyMode = $modifyMode;
        return $this;
    }


    /**
     * 修改模式 删除后新增
     */
    const MODIFY_MODE_DELETE_ADD = 1;

    /**
     * 修改模式 修改
     */
    const MODIFY_MODE_UPDATE = 2;

    private $modifyMode = 1;

    private $oldData = [];

    /**
     * @var Builder
     */
    private $model;
    private $foreignId = '';
    private $foreignField;
    private $deleteHandler = [];
    private $updateHandler = [];
    /**
     * @var Closure
     */
    private $uniqueCall;
    /**
     * @var Closure
     */
    private $queryCall;
    private $additional_fields = [];
    private $commonData = [];
    private $childNode = [];
}